#ifndef AFCORE_COMMON_ALGORITHM_PC_QUEUE_
#define AFCORE_COMMON_ALGORITHM_PC_QUEUE_

#include <condition_variable>
#include <mutex>
#include <queue>
#include <atomic>
#include <type_traits>

namespace afcore {

/// @brief  ProducerConsumerQueue
template<typename T>
class CPcQueue {
  template<typename E = T>
  typename std::enable_if<std::is_pointer<E>::value>::type DeleteQueuedObject(E& obj) { delete obj; }
  template<typename E = T>
  typename std::enable_if<!std::is_pointer<E>::value>::type DeleteQueuedObject(E const& /*packet*/) { }
public:
  /// @brief  构造函数
  CPcQueue<T>()
    : shutdown_(false) {
  }

  /// @brief  入队
  /// @param  value       入队元素
  void Enqueue(const T& value) {
    std::lock_guard<std::mutex> lock(queue_mutex_);
    q_.emplace(std::move(value));
    cv_.notify_one();
  }

  /// @brief  队列是否为空
  /// @return true        队空
  /// @return false       非队空
  bool Empty() {
    std::lock_guard<std::mutex> lock(queue_mutex_);
    return q_.empty();
  }

  /// @brief  出队 非阻塞
  /// @param  value       出队元素
  /// @return true        出队成功
  /// @return false       出队失败
  bool Dequeue(T& value) {
    std::lock_guard<std::mutex> lock(queue_mutex_);

    if (q_.empty() || shutdown_) {
      return false;
    }

    value = q_.front();
    q_.pop();

    return true;
  }

  /// @brief  出队 阻塞
  /// @param  value       出队元素
  void DequeueWait(T& value) {
    std::unique_lock<std::mutex> lock(queue_mutex_);

    // we could be using .wait(lock, predicate) overload here but it is broken
    // https://connect.microsoft.com/VisualStudio/feedback/details/1098841
    while (q_.empty() && !shutdown_) {
      cv_.wait(lock);
    }

    value = q_.front();
    q_.pop();
  }

  /// @brief  取消 队列中所有元素销毁
  void Cancel() {
    std::unique_lock<std::mutex> lock(queue_mutex_);

    while (!q_.empty()) {
      T& value = q_.front();
      DeleteQueuedObject(value);
      q_.pop();
    }

    shutdown_ = true;
    cv_.notify_all();
  }

private:
  std::mutex queue_mutex_;      ///< 队列互斥锁
  std::queue<T> q_;             ///< 队列
  std::condition_variable cv_;  ///< 条件
  std::atomic<bool> shutdown_;  ///< 是否关闭
};

} // !namespace afcore

#endif //! AFCORE_COMMON_ALGORITHM_PC_QUEUE_